/**
 * File: MaqMatchProcessor.java
 * Created by: mhaimel
 * Created on: 7 Jul 2009
 * CVS:  $Id: MaqMatchProcessor.java,v 1.3 2009/12/07 10:00:54 mhaimel Exp $
 */
package uk.ac.ebi.curtain.processor.maq.impl;

import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import uk.ac.ebi.curtain.controller.maq.MaqMatchInputBean;
import uk.ac.ebi.curtain.processor.AbstractProcessor;
import uk.ac.ebi.curtain.service.IdentifierFilepositionIndex;
import uk.ac.ebi.curtain.util.ExecutionService;
import uk.ac.ebi.curtain.util.FileIndexWriter;
import uk.ac.ebi.curtain.utils.CurtainUncheckedException;
import uk.ac.ebi.curtain.utils.data.DataEntry;
import uk.ac.ebi.curtain.utils.data.FileType;
import uk.ac.ebi.curtain.utils.data.MetaData;
import uk.ac.ebi.curtain.utils.data.impl.GenericDataHolder;
import uk.ac.ebi.curtain.utils.file.FileInfo;
import uk.ac.ebi.curtain.utils.io.impl.FileIO;

import com.google.inject.Inject;

/**
 * @author mhaimel
 *
 */
public class MaqMatchProcessor extends AbstractProcessor<Serializable, DataEntry<File>> {

	private static final String MATCH_PAIRED_OPT = "-a";
	private static final String MATCH_PAIRED_OPT2 = "-A";
	private static final String MATCH_OPT = "match";
	private static final String MAPVIEW_NO_SEQU_OPT = "-b";
	private static final String MAPVIEW_OPT = "mapview";
	
	private ExecutionService executionService;
	private DataEntry<File> fi;
	private IdentifierFilepositionIndex confIdx;

	@Override
	protected DataEntry<File> buildReturnObject() {
		return this.fi;
	}
	
	private MaqMatchInputBean getBeanInput(){
		return (MaqMatchInputBean) getInput();
	}
	
	private IdentifierFilepositionIndex getConfigIdx(){
		if(null == confIdx){
			confIdx = getContext().getPositionIndex(getContext().currentFiles().getConfigurationInformation());
		}
		return confIdx;
	}

	@Override
	public void process() {
		this.fi = null;
		File mFile = matchFiles(getBeanInput());
		File txtFile = map2Txt(getBeanInput().getDir(), mFile);
		FileInfo sampleReadfile = getBeanInput().getReadFiles().get(0);
		Integer cat = getContext().getArguments().getCategory(sampleReadfile);
		FileInfo tmpFi = new FileInfo(txtFile,FileType.maq,sampleReadfile.getReadType());
		getContext().getArguments().addFile(tmpFi);
		this.fi = new GenericDataHolder<File>(new MetaData(cat, FileType.maq, sampleReadfile.getReadType()), txtFile);
		getConfigIdx().setWriteOnly();
		FileIndexWriter writer = null;
		try{
			Integer fId = getContext().getGlobalFileIndex().register(tmpFi);
			writer = getConfigIdx().getWriter();
			writer.addFileId(fId, tmpFi);
			writer.addFile(tmpFi, cat);
		}finally{
			FileIO.closeQuietly(writer);
		}		
	}
	
	public File map2Txt(File baseDir, File mapFile) {
		getLog().debug("Extract mapping information into output file ...");
		File txtFile = new File(baseDir,mapFile.getName()+".txt");
		List<String> cmds = new ArrayList<String>();
		cmds.add(getConfig().getMaqExec());
		cmds.add(MAPVIEW_OPT);
		cmds.add(MAPVIEW_NO_SEQU_OPT);
		cmds.add(mapFile.getAbsolutePath());
		
		File errF = getNextFile(baseDir, txtFile.getName()+"_", ".err");
		exec(baseDir, cmds.toArray(new String[0]), txtFile, errF);
		if(txtFile.length() <= 0){
			getLog().warn("No Mapping information extracted from " + mapFile);
		}
		return txtFile;
	}
	
	public File matchFiles(MaqMatchInputBean bean) {
		getLog().debug("Matching bfq files to bfa file ...");
		List<File> bfqFileList = bean.getBfqPair();
		if(bfqFileList.size() > 2 || bfqFileList.isEmpty()){
			throw new CurtainUncheckedException("Invalid BFQ files provided: " + bfqFileList.size() + "; Files: " + bfqFileList);
		}
		
		File mapFile = buildMapFile(bean.getDir(), bean.getBfa(), bfqFileList.get(0));
		List<String> cmds = new ArrayList<String>();
		cmds.add(getConfig().getMaqExec());
		cmds.add(MATCH_OPT);
		FileInfo readFile = bean.getReadFiles().get(0);
		if(readFile.getReadType().isPaired()){
			Integer insLen = getInsertLength(readFile);
			if(insLen == null){
				throw new CurtainUncheckedException("No insert length provided for paired file " + readFile);
			}
			if(bfqFileList.size() != 2 || insLen == null){
				throw new CurtainUncheckedException("Two bfq files expected for paired read input " + readFile+": provided with " + bfqFileList);
			}
			getLog().debug("User insert length information for maq of " + insLen);
			// use read paired information
			String insLim = Integer.valueOf(Double.valueOf(insLen*1.4).intValue()).toString();
			cmds.add(MATCH_PAIRED_OPT); // F<->R
			cmds.add(insLim);
			cmds.add(MATCH_PAIRED_OPT2); // allow also R<->F 
			cmds.add(insLim);
		}
		cmds.add(mapFile.getAbsolutePath());
		cmds.add(bean.getBfa().getAbsolutePath());
		for(File f : bfqFileList){
			cmds.add(f.getAbsolutePath());
		}
		File logF = getNextFile(bean.getDir(), mapFile.getName()+"_", ".log");
		File errF = getNextFile(bean.getDir(), mapFile.getName()+"_", ".err");
		exec(bean.getDir(), cmds.toArray(new String[0]), logF, errF,false,true);
		getLog().debug("Finished matching bfq files to bfa file!");
		return mapFile;
	}

	private Integer getInsertLength(FileInfo readFile) {
		return getContext().getArguments().getInsertLength(readFile);
	}

	private File buildMapFile(File baseDir, File bfa, File bfq) {
		return new File(baseDir,bfq.getName()+"_"+bfa.getName()+".map");
	}	
	
	private int exec(File baseDir, String[] cmdArr, File logFile, File errFile) {
		return executionService.exec(baseDir, cmdArr, logFile, errFile);
	}

	private int exec(File baseDir, String[] cmdArr, File logFile, File errFile,
			boolean failOnErroroutput, boolean failOnExitCode) {
		return executionService.exec(baseDir, cmdArr, logFile, errFile,
				failOnErroroutput, failOnExitCode);
	}

	private File getNextFile(File dir, String prefix, String postfix) {
		return executionService.getNextFile(dir, prefix, postfix);
	}

	@Inject
	public void setExecutionService(ExecutionService executionService) {
		this.executionService = executionService;
	}

	protected ExecutionService getExecutionService() {
		return executionService;
	}
}
